

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <getopt.h>

#include <sys/socket.h>
#include <sys/time.h>

#include <arpa/inet.h>

#define LOG(_fmt, ...) fprintf(stdout, "[%s:%d]: " _fmt, __FILE__, __LINE__, ##__VA_ARGS__) // 如果没有参数（如示例 1），##__VA_ARGS__ 会自动消除多余的逗号，避免语法错误。

#define MAX_MAS_LENGTH 512
#define TIME_SUB_MS(tv1, tv2) ((tv1.tv_sec - tv2.tv_sec) * 1000 + (tv1.tv_usec - tv2.tv_usec) / 1000)

int send_msg(int connfd, char *msg, int length)
{

	int res = send(connfd, msg, length, 0);
	if (res < 0)
	{
		perror("send");
		exit(1);
	}
	return res;
}

int recv_msg(int connfd, char *msg, int length)
{

	int res = recv(connfd, msg, length, 0);
	if (res < 0)
	{
		perror("recv");
		exit(1);
	}
	return res;
}

void equals(char *pattern, char *result, char *casename)
{

	if (strcmp(pattern, result) == 0)
	{
		// printf("==> PASS --> %s\n", casename);
	}
	else
	{
		printf("==> FAILED --> %s, '%s' != '%s'\n", casename, pattern, result);
	}
}

void test_case(int connfd, char *msg, char *pattern, char *casename)
{

	if (!msg || !pattern || !casename)
		return;

	send_msg(connfd, msg, strlen(msg));
	// LOG("send: %s\n", msg);

	char result[MAX_MAS_LENGTH] = {0};
	recv_msg(connfd, result, MAX_MAS_LENGTH);

	// LOG("recv: %s\n", result);
	equals(pattern, result, casename);
}

void array_testcase(int connfd)
{

	test_case(connfd, "SET Name D", "SET SUCCESS", "SETCase");
	test_case(connfd, "GET Name", "D", "GETCase");
	test_case(connfd, "MOD Name C", "MOD SUCCESS", "MODCase");
	test_case(connfd, "GET Name", "C", "GETCase");
	test_case(connfd, "DEL Name", "DEL SUCCESS", "DELCase");
	test_case(connfd, "GET Name", "NO EXIST", "GETCase");
}

void array_testcase_10w(int connfd)
{ // 10w

	int count = 100000;
	int i = 0;

	while (i++ < count)
	{
		array_testcase(connfd);
	}
}

void rbtree_testcase(int connfd)
{

	test_case(connfd, "RSET Name D", "RSET SUCCESS", "SETCase");
	test_case(connfd, "RGET Name", "D", "GETCase");
	test_case(connfd, "RMOD Name C", "RMOD SUCCESS", "MODCase");
	test_case(connfd, "RGET Name", "C", "GETCase");
	test_case(connfd, "RDEL Name", "RDEL SUCCESS", "DELCase");
	test_case(connfd, "RGET Name", "NO REXIST", "GETCase");
}

void rbtree_testcase_10w(int connfd)
{ // 10w

	int count = 100000;
	int i = 0;

	while (i++ < count)
	{
		array_testcase(connfd);
	}
}

void rbtree_testcase_5w_node(int connfd)
{

	int count = 50000;
	int i = 0;

	for (i = 0; i < count; i++)
	{

		char cmd[128] = {0};

		snprintf(cmd, 128, "RSET Name%d DCF%d", i, i);
		test_case(connfd, cmd, "RSET SUCCESS", "SETCase");

		char result[128] = {0};
		sprintf(result, "RCOUNT: %d", i + 1);
		test_case(connfd, "RCOUNT", result, "RCOUNT");
	}

	for (i = 0; i < count; i++)
	{
		char cmd[128];

		snprintf(cmd, 128, "RGET Name%d", i);
		char result[128] = {0};
		sprintf(result, "DCF%d", i);
		test_case(connfd, cmd, result, "RGET");
	}

	for (i = 0; i < count; i++)
	{

		char cmd[128] = {0};

		snprintf(cmd, 128, "RDEL Name%d", i);
		test_case(connfd, cmd, "RDEL SUCCESS", "DELCase");

		char result[128] = {0};
		sprintf(result, "RCOUNT: %d", count - (i + 1));
		test_case(connfd, "RCOUNT", result, "RCOUNT");
	}
}

void hash_testcase(int connfd)
{

	test_case(connfd, "HSET Name D", "HSET SUCCESS", "HSETCase");
	test_case(connfd, "HGET Name", "D", "HGETCase");
	test_case(connfd, "HMOD Name C", "SMOD SUCCESS", "HMODCase");
	test_case(connfd, "HGET Name", "C", "HGETCase");
	test_case(connfd, "HDEL Name", "HDEL SUCCESS", "HDELCase");
	test_case(connfd, "HGET Name", "NO HEXIST", "HGETCase");
}

void hash_testcase_10w(int connfd)
{ // 10w

	int count = 100000;
	int i = 0;

	while (i++ < count)
	{
		hash_testcase(connfd);
	}
}

void hash_testcase_5w_node(int connfd)
{

	int count = 50000;
	int i = 0;

	for (i = 0; i < count; i++)
	{

		char cmd[128] = {0};

		snprintf(cmd, 128, "HSET Name%d DCF%d", i, i);
		test_case(connfd, cmd, "HSET SUCCESS", "SETCase");

		char result[128] = {0};
		sprintf(result, "HCOUNT: %d", i + 1);
		test_case(connfd, "HCOUNT", result, "HCOUNT");
	}

	for (i = 0; i < count; i++)
	{
		char cmd[128];

		snprintf(cmd, 128, "HGET Name%d", i);
		char result[128] = {0};
		sprintf(result, "DCF%d", i);
		test_case(connfd, cmd, result, "HGET");
	}

	for (i = 0; i < count; i++)
	{

		char cmd[128] = {0};

		snprintf(cmd, 128, "HDEL Name%d", i);
		test_case(connfd, cmd, "HDEL SUCCESS", "DELCase");

		char result[128] = {0};
		sprintf(result, "HCOUNT: %d", count - (i + 1));
		test_case(connfd, "HCOUNT", result, "HCOUNT");
	}
}


void skiplist_testcase(int connfd)
{

	test_case(connfd, "SSET Name D", "SSET SUCCESS", "HSETCase");
	test_case(connfd, "SGET Name", "D", "HGETCase");
	test_case(connfd, "SMOD Name C", "SMOD SUCCESS", "HMODCase");
	test_case(connfd, "SGET Name", "C", "HGETCase");
	test_case(connfd, "SDEL Name", "SEL SUCCESS", "HDELCase");
	test_case(connfd, "SGET Name", "NO SEXIST", "HGETCase");
}

void skiplist_testcase_5w_node(int connfd)
{

	int count = 50000;
	int i = 0;

	for (i = 0; i < count; i++)
	{

		char cmd[128] = {0};

		snprintf(cmd, 128, "SSET Name%d DCF%d", i, i);
		test_case(connfd, cmd, "SSET SUCCESS", "SETCase");

		char result[128] = {0};
		sprintf(result, "SCOUNT: %d", i + 1);
		test_case(connfd, "SCOUNT", result, "SCOUNT");
	}

	for (i = 0; i < count; i++)
	{
		char cmd[128];

		snprintf(cmd, 128, "SGET Name%d", i);
		char result[128] = {0};
		sprintf(result, "DCF%d", i);
		test_case(connfd, cmd, result, "SGET");
	}

	for (i = 0; i < count; i++)
	{

		char cmd[128] = {0};

		snprintf(cmd, 128, "SDEL Name%d", i);
		test_case(connfd, cmd, "SDEL SUCCESS", "DELCase");

		char result[128] = {0};
		sprintf(result, "SCOUNT: %d", count - (i + 1));
		test_case(connfd, "SCOUNT", result, "SCOUNT");
	}
}

int connect_tcpserver(const char *ip, unsigned short port)
{

	int connfd = socket(AF_INET, SOCK_STREAM, 0);

	struct sockaddr_in tcpserver_addr;
	memset(&tcpserver_addr, 0, sizeof(struct sockaddr_in));

	tcpserver_addr.sin_family = AF_INET;
	tcpserver_addr.sin_addr.s_addr = inet_addr(ip);
	tcpserver_addr.sin_port = htons(port);

	int ret = connect(connfd, (struct sockaddr *)&tcpserver_addr, sizeof(struct sockaddr_in));
	if (ret)
	{
		perror("connect");
		return -1;
	}

	return connfd;
}


// ./testcase -s 192.168.243.131 -p 9096 -m 1
int main(int argc, char *argv[])
{

	int ret = 0;

	char ip[16] = {0};
	int port = 0;
	int mode = 1;

	int opt;
	while ((opt = getopt(argc, argv, "s:p:m:?")) != -1)
	{

		switch (opt)
		{

		case 's':
			strcpy(ip, optarg);
			break;

		case 'p':
			port = atoi(optarg);
			break;

		case 'm':
			mode = atoi(optarg);
			break;

		default:
			return -1;
		}
	}

	int connfd = connect_tcpserver(ip, port);

	if (mode == 1)
	{ // array

		struct timeval tv_begin;
		gettimeofday(&tv_begin, NULL);

		array_testcase_10w(connfd);

		struct timeval tv_end;
		gettimeofday(&tv_end, NULL);

		int time_used = TIME_SUB_MS(tv_end, tv_begin);

		printf("array testcase--> time_used: %d, qps: %d\n", time_used, 600000 * 1000 / time_used);
	}

	if (mode == 2)
	{ // rbtree

		struct timeval tv_begin;
		gettimeofday(&tv_begin, NULL);

		// rbtree_testcase_10w(connfd);

		rbtree_testcase_5w_node(connfd);

		struct timeval tv_end;
		gettimeofday(&tv_end, NULL);

		int time_used = TIME_SUB_MS(tv_end, tv_begin);

		printf("rbtree testcase-->  time_used: %d, qps: %d\n", time_used, 250000 * 1000 / time_used);
	}

	if (mode == 3)
	{ // hash

		struct timeval tv_begin;
		gettimeofday(&tv_begin, NULL);

		// hash_testcase(connfd);
		hash_testcase_5w_node(connfd);

		struct timeval tv_end;
		gettimeofday(&tv_end, NULL);

		int time_used = TIME_SUB_MS(tv_end, tv_begin);

		printf("hash testcase-->  time_used: %d, qps: %d\n", time_used, 250000 * 1000 / time_used);
	}

	if (mode == 4)
	{ // skiplist

		struct timeval tv_begin;
		gettimeofday(&tv_begin, NULL);

		// skiplist_testcase(connfd);
		skiplist_testcase_5w_node(connfd);

		struct timeval tv_end;
		gettimeofday(&tv_end, NULL);

		int time_used = TIME_SUB_MS(tv_end, tv_begin);

		printf("hash testcase-->  time_used: %d, qps: %d\n", time_used, 250000 * 1000 / time_used);
	}
}
